/*
 * Powered By XY
 * Since 2020
 */

package com.xy.biz.uc.manager.impl;

import com.xy.biz.uc.dao.RoleDao;
import com.xy.biz.uc.dao.UserGroupDao;
import com.xy.biz.uc.dao.UserGroupRoleRelDao;
import com.xy.biz.uc.dao.UserGroupUserRelDao;
import com.xy.biz.uc.domain.bo.RoleBO;
import com.xy.biz.uc.domain.bo.UserBO;
import com.xy.biz.uc.domain.bo.UserGroupBO;
import com.xy.biz.uc.domain.converter.RoleConverter;
import com.xy.biz.uc.domain.converter.UserGroupConverter;
import com.xy.biz.uc.domain.po.UserGroupPO;
import com.xy.biz.uc.domain.po.UserGroupRoleRelPO;
import com.xy.biz.uc.domain.po.UserGroupUserRelPO;
import com.xy.biz.uc.domain.query.UserGroupIdQuery;
import com.xy.biz.uc.domain.query.UserGroupQuery;
import com.xy.biz.uc.domain.req.ListUserGroupItemRequest;
import com.xy.biz.uc.domain.resp.GetUserGroupResponse;
import com.xy.biz.uc.domain.resp.ListUserGroupItemResponse;
import com.xy.biz.uc.manager.UserGroupManager;
import com.xy.lang.util.CollectionUtilsEx;
import com.xy.tool.context.UserContextHolder;
import com.xy.tool.util.BeanUtilsEx;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 
 * 
 * @author zsp
 * @date 2020-8-20
 */
@Service
public class UserGroupManagerImpl implements UserGroupManager {

	@Autowired
	private UserGroupDao userGroupDao;
	@Autowired
	private UserGroupUserRelDao userGroupUserRelDao;
	@Autowired
	private UserGroupRoleRelDao userGroupRoleRelDao;
	@Autowired
	private RoleDao roleDao;

	@Transactional
	@Override
	public int saveUserGroup(UserGroupBO userGroupBO) {
		UserGroupPO userGroupPO = UserGroupConverter.toPO(userGroupBO);
		int affectedRows = userGroupDao.saveUserGroup(userGroupPO);
		Set<UserBO> userBOSet;
		if(affectedRows > 0 && !CollectionUtils.isEmpty(userBOSet = userGroupBO.getUserBOSet())) {
			userGroupUserRelDao.saveAllUserGroupUserRel(userBOSet.stream().map(
					e -> UserGroupUserRelPO.builder()
							.userGroupId(userGroupPO.getUserGroupId())
							.userId(e.getUserId())
							.joinStatus(e.getJoinStatus())
							.joinTime(e.getJoinTime())
							.createUserId(userGroupBO.getCreateUserId())
							.createTime(userGroupBO.getCreateTime())
							.build())
					.collect(Collectors.toList()));
		}
		List<Long> roleIdList;
		if(affectedRows > 0 && !CollectionUtils.isEmpty(roleIdList = userGroupBO.getRoleIdList())) {
			userGroupRoleRelDao.saveAllUserGroupRoleRel(roleIdList.stream().map(
					e -> UserGroupRoleRelPO.builder()
							.userGroupId(userGroupPO.getUserGroupId())
							.roleId(e)
							.build())
					.collect(Collectors.toList()));
		}
		return affectedRows;
	}

	@Transactional
	@Override
	public int updateUserGroup(UserGroupBO userGroupBO) {
		int affectedRows = userGroupDao.updateUserGroup(UserGroupConverter.toPO(userGroupBO));
		List<Long> roleIdList;
		if(affectedRows > 0) {
			userGroupRoleRelDao.deleteUserGroupRoleRelByUserGroupId(userGroupBO.getUserGroupId());
			if(!CollectionUtils.isEmpty(roleIdList = userGroupBO.getRoleIdList())) {
				userGroupRoleRelDao.saveAllUserGroupRoleRel(roleIdList.stream().map(
						e -> UserGroupRoleRelPO.builder().userGroupId(userGroupBO.getUserGroupId()).roleId(e).build())
						.collect(Collectors.toList()));
			}
		}
		return affectedRows;
	}

	@Transactional
	@Override
	public int deleteUserGroup(Long userGroupId) {
		int affectedRows = userGroupDao.deleteUserGroup(userGroupId);
		if(affectedRows > 0) {
			userGroupRoleRelDao.deleteUserGroupRoleRelByUserGroupId(userGroupId);
		}
		return affectedRows;
	}
	
	@Override
	public UserGroupBO getUserGroup(UserGroupIdQuery query) {
		Long userGroupId = query.firstUserGroupId();
		UserGroupBO userGroupBO = UserGroupConverter.fromPO(userGroupDao.getUserGroup(userGroupId));
		if(userGroupBO != null) {
			if(query.isWithRole()) {
				List<UserGroupRoleRelPO> userGroupRoleRelPOList = userGroupRoleRelDao.listUserGroupRoleRelByUserGroupId(Arrays.asList(userGroupId));
				if (!CollectionUtils.isEmpty(userGroupRoleRelPOList)) {
					Map<Long, List<UserGroupRoleRelPO>> relGroup = userGroupRoleRelPOList.stream().collect(Collectors.groupingBy(UserGroupRoleRelPO::getRoleId));
					if (query.isWithRole()) {
						Map<Long, Set<RoleBO>> map = findRoleForUserGroup(relGroup);
						userGroupBO.setRoleBOSet(map.get(userGroupId));
					}
				}
			}
		}
		return userGroupBO;
	}
	
	@Override
	public List<UserGroupBO> listUserGroupById(UserGroupIdQuery query) {
		List<UserGroupBO> userGroupBOList = UserGroupConverter.fromPO(userGroupDao.listUserGroupById(query.getUserGroupIdList()));
		if(!CollectionUtils.isEmpty(userGroupBOList)) {
			if(query.isWithRole()) {
				List<UserGroupRoleRelPO> userGroupRoleRelPOList = userGroupRoleRelDao.listUserGroupRoleRelByUserGroupId(query.getUserGroupIdList());
				if (!CollectionUtils.isEmpty(userGroupRoleRelPOList)) {
					Map<Long, List<UserGroupRoleRelPO>> relGroup = userGroupRoleRelPOList.stream().collect(Collectors.groupingBy(UserGroupRoleRelPO::getRoleId));
					if (query.isWithRole()) {
						Map<Long, Set<RoleBO>> map = findRoleForUserGroup(relGroup);
						for (UserGroupBO userGroupBO : userGroupBOList) {
							userGroupBO.setRoleBOSet(map.get(userGroupBO.getUserGroupId()));
						}
					}
				}
			}
		}
		return userGroupBOList;
	}

	private Map<Long, Set<RoleBO>> findRoleForUserGroup(Map<Long, List<UserGroupRoleRelPO>> relGroup) {
		List<RoleBO> roleBOList = RoleConverter.fromPO(roleDao.listRoleById(relGroup.keySet()));
		if(!CollectionUtils.isEmpty(roleBOList)) {
			Map<Long, Set<RoleBO>> userGroupWithRoleMap = new HashMap<>();
			for(RoleBO roleBO : roleBOList) {
				List<UserGroupRoleRelPO> relGroupMember = relGroup.get(roleBO.getRoleId());
				if(!CollectionUtils.isEmpty(relGroupMember)) {
					for (UserGroupRoleRelPO userGroupRoleRelPO : relGroupMember) {
						Set<RoleBO> set = userGroupWithRoleMap.computeIfAbsent(userGroupRoleRelPO.getUserGroupId(), e -> new HashSet<>());
						set.add(roleBO);
					}
				}
			}
			return userGroupWithRoleMap;
		}

		return Collections.emptyMap();
	}
	
	@Override
	public List<UserGroupBO> listUserGroup(UserGroupQuery query) {
		List<UserGroupBO> userGroupBOList = UserGroupConverter.fromPO(userGroupDao.listUserGroup(query));
		if(!CollectionUtils.isEmpty(userGroupBOList)) {
			if(query.isWithParent()) {
				//TODO
				CollectionUtilsEx.foreachSetParent(userGroupBOList,
						UserGroupBO::getUserGroupId,
						UserGroupBO::getParentUserGroupId,
						e -> UserGroupConverter.fromPO(userGroupDao.listUserGroupById(e)),
						(userGroupBO, parentUserGroupBO) -> userGroupBO.setParentUserGroup(parentUserGroupBO));
			}
			if(query.isWithRole()) {
				List<Long> userGroupIdList = userGroupBOList.stream().map(UserGroupBO::getUserGroupId).collect(Collectors.toList());
				List<UserGroupRoleRelPO> userGroupRoleRelPOList = userGroupRoleRelDao.listUserGroupRoleRelByUserGroupId(userGroupIdList);
				if (!CollectionUtils.isEmpty(userGroupRoleRelPOList)) {
					Map<Long, List<UserGroupRoleRelPO>> relGroup = userGroupRoleRelPOList.stream().collect(Collectors.groupingBy(UserGroupRoleRelPO::getRoleId));
					if (query.isWithRole()) {
						Map<Long, Set<RoleBO>> map = findRoleForUserGroup(relGroup);
						for (UserGroupBO userGroupBO : userGroupBOList) {
							userGroupBO.setRoleBOSet(map.get(userGroupBO.getUserGroupId()));
						}
					}
				}
			}
		}

		return userGroupBOList;
	}
	
	@Override
	public int countUserGroup(UserGroupQuery query) {
		return userGroupDao.countUserGroup(query);
	}

	@Override
	public List<ListUserGroupItemResponse> listUserGroupItem(ListUserGroupItemRequest request) {
		UserGroupQuery userGroupQuery = BeanUtilsEx.copyProperties(request, () -> UserGroupQuery.max().build());
		userGroupQuery.setTenantId(UserContextHolder.getTenantId());
		List<ListUserGroupItemResponse> listUserGroupItemResponseList = BeanUtilsEx.copyPropertiesForNewList(
				userGroupDao.listUserGroup(userGroupQuery), () -> new ListUserGroupItemResponse());
		if(!CollectionUtils.isEmpty(listUserGroupItemResponseList)) {
			listUserGroupItemResponseList.sort(Comparator.comparing(ListUserGroupItemResponse::getSequence));
		}
		return listUserGroupItemResponseList;
	}

	@Override
	public List<Long> listRoleIdByUserGroupId(Long userGroupId) {
		List<UserGroupRoleRelPO> userGroupRoleRelPOList = userGroupRoleRelDao.listUserGroupRoleRelByUserGroupId(Arrays.asList(userGroupId));
		if(!CollectionUtils.isEmpty(userGroupRoleRelPOList)) {
			return userGroupRoleRelPOList.stream().map(UserGroupRoleRelPO::getRoleId).collect(Collectors.toList());
		}
		return Collections.emptyList();
	}

	@Override
	public List<GetUserGroupResponse> listUserGroupBy(UserGroupQuery query) {
		return userGroupDao.listUserGroupBy(query);
	}

	/**
	 *
	 * @param groupName
	 * @return
	 */
	@Override
	public List<UserGroupBO> listUserGroupByGroupName(String groupName) {
		return UserGroupConverter.fromPO(userGroupDao.listUserGroupByName(groupName));
	}

	@Override
	public List<Long> listParentUserGroupId(Long userGroupId) {
		return userGroupDao.listParentUserGroupId(userGroupId);
	}

	@Override
	public List<Long> listChildrenUserGroupId(Long userGroupId) {
		return userGroupDao.listChildrenUserGroupId(userGroupId);
	}

}
